#pragma once

#include <map>
#include <string>
#include <cstdint>

struct DownloadTask;
typedef void CURLM;

class Downloader {
public:
    enum State {
        Paused,
        Downloading,
        Finished,
        Cancelled,
        ErrorOccured,
    };

    struct DownloadInfo {
        uint64_t filetime;
        uint64_t curr;
        uint64_t total;
        State state;
        std::string url;
    };

public:
    Downloader();
    virtual ~Downloader();

    void process();

    DownloadTask *addUrl(const std::string &url, bool paused = false);
    DownloadTask *addUrlFile(const std::string &url, const std::string &localfile, bool paused = false);
    DownloadTask *addUrlHead(const std::string &url);
    inline void setProxy(const std::string &proxy) { proxy_ = proxy; }
    bool resume(DownloadTask *dt);
    bool pause(DownloadTask *dt);
    bool cancel(DownloadTask *dt);
    static State state(DownloadTask *dt);
    static void *getUserData(DownloadTask *dt);
    static uintptr_t getUserParam(DownloadTask *dt);
    static void setUserData(DownloadTask *dt, void *data);
    static void setUserParam(DownloadTask *dt, uintptr_t param);
    static void setFinishCallback(DownloadTask *dt, void(*finishCallback)(DownloadTask*));
    static void setProgressCallback(DownloadTask *dt, void(*progressCallback)(DownloadTask*, uint64_t, uint64_t));
    static bool getInfo(DownloadTask *dt, DownloadInfo &di);
    static bool getContent(DownloadTask *dt, std::string &content);

private:
    DownloadTask *addCommon(const std::string &url, bool paused = false, uint64_t resume = 0ull);
    void addCommon(DownloadTask *dt, bool paused = false, uint64_t resume = 0ull);

private:
    static size_t _writecb(void *ptr, size_t size, size_t nmemb, void *data);
    static int _progcb(void *data, uint64_t dltotal, uint64_t dlnow, uint64_t ultotal, uint64_t ulnow);
    void writeCb(DownloadTask *dt, const void *ptr, size_t sz);
    void progressCb(DownloadTask *dt, uint64_t curr, uint64_t sz);

private:
    std::map<int, DownloadTask*> tasks_;
    std::string proxy_;
    CURLM *handle_ = nullptr;
    int running_ = 0;
    int uid_ = 0;
};
